package com.sgb.knowledgeshare.weight;



import android.annotation.SuppressLint;

import android.content.Context;

import android.content.res.Resources;

import android.util.AttributeSet;

import android.util.Log;

import android.view.MotionEvent;

import android.view.View;

import android.view.ViewConfiguration;

import android.view.ViewGroup;



import androidx.annotation.IntDef;

import androidx.annotation.NonNull;

import androidx.annotation.Nullable;

import androidx.core.view.ViewCompat;

import androidx.customview.widget.ViewDragHelper;



import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;



/**

 * Created by WYJ on 2020/9/25.

 */

public class RightDrawerLayout extends ViewGroup {



    private static final int MIN_DRAWER_MARGIN = 64; // dp

    /**

     * Minimum velocity that will be detected as a fling

     */

    private static final int MIN_FLING_VELOCITY = 400; // dips per second



    /**

     * drawer离父容器右边的最小外边距

     */

    private int mMinDrawerMargin;



    private View mMenuView;

    private View mMainView;

    private int mDrawerState = STATE_IDLE;

    private boolean isDrawerClosed = true;

    private MyViewDragHelper mHelper;

    private DrawerListener mListener;



    /**

     * drawer显示出来的占自身的百分比

     */

    private float mMenuOnScreen;



    private int mTouchSlop;





    public RightDrawerLayout(@NonNull Context context) {

        this(context, null);

    }



    public RightDrawerLayout(@NonNull Context context, @Nullable AttributeSet attrs) {

        this(context, attrs, 0);

    }



    public RightDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);



        //setup drawer's minMargin

        final float density = getResources().getDisplayMetrics().density;

        final float minVel = MIN_FLING_VELOCITY * density;

        mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f);

        mHelper = MyViewDragHelper.create(this, 1.0f, new MyViewDragHelper.Callback() {



            @Override

            public boolean tryCaptureView(@NonNull View child, int pointerId) {

                Log.e("ViewDragHelper", "tryCaptureView");

                return child == mMenuView;

            }



            @Override

            public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {

                final int width = getWidth();

                return Math.max(width - child.getWidth(), Math.min(left, width));

            }



            @Override

            public void onEdgeDragStarted(int edgeFlags, int pointerId) {

                Log.e("ViewDragHelper", "onEdgeDragStarted");

                mHelper.captureChildView(mMenuView, pointerId);

            }



            @Override

            public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {

                Log.e("ViewDragHelper", "onViewReleased : " + xvel);

                if (Math.abs(xvel) < Math.abs(yvel) && Math.abs(xvel) < 5) {

                    return;

                }

                final int childWidth = mMenuView.getWidth();

                final int width = getWidth();

                int left = xvel < 0 || (xvel == 0 && mMenuOnScreen == 1.0f) ? width - childWidth : width;

                mHelper.settleCapturedViewAt(left, 0);

                invalidate();

            }



            @Override

            public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {

                final int childWidth = changedView.getWidth();

                final int width = getWidth();

                if (Math.abs(left) >= (getWidth() - mMenuView.getMeasuredWidth()) || Math.abs(left) <= width) {

                    //offset can callback here

                    float offset = (float) (width - left) / childWidth;

                    setDrawerViewOffset(changedView, offset);

                    mMenuView.setVisibility(offset == 0 ? View.INVISIBLE : View.VISIBLE);

                    invalidate();

                }

            }



            @Override

            public int getViewHorizontalDragRange(@NonNull View child) {

                return mMenuView == child ? child.getWidth() : 0;

            }



            @Override

            public int clampViewPositionVertical(@NonNull View child, int top, int dy) {

                return 0;

            }



            @Override

            public void onViewDragStateChanged(int state) {

                updateDrawerState(state, mHelper.getCapturedView());

            }

        });



        //设置edge_left track

        mHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT);

        mHelper.setEdgeSize(getSWidth());

        //设置minVelocity

        mHelper.setMinVelocity(minVel);



        final ViewConfiguration configuration = ViewConfiguration.get(getContext());

        mTouchSlop =configuration.getScaledTouchSlop();

    }



    public void setDrawerViewOffset(View drawerView, float slideOffset) {

        if (slideOffset == mMenuOnScreen) {

            return;

        }

        mMenuOnScreen = slideOffset;

        if (mListener != null) {

            mListener.onDrawerSlide(drawerView, slideOffset);

        }

        if (mMenuOnScreen == 1.0 || mMenuOnScreen == 0.0f) {

            mHelper.cancel();

        }

        scale(drawerView, slideOffset);

    }



    public void scale(View drawerView, float slideOffset) {

        View mContent = getChildAt(0);

        float scale = drawerView.getWidth() / (float) getWidth();

        float inverseX = 1 - scale * slideOffset;

        float inverseY = 1 - scale * slideOffset * 0.5f;

        mContent.setTranslationX(-(int) (drawerView.getWidth() * slideOffset) * 0.5f);

        mContent.setScaleX(inverseX);

        mContent.setScaleY(inverseY);

        //drawerView.setScaleY(inverseY);

        drawerView.setAlpha(slideOffset);

    }



    void updateDrawerState(@RightDrawerLayout.State int activeState, View activeDrawer) {

        final int rightState = mHelper.getViewDragState();



        final int state;

        if (rightState == STATE_DRAGGING) {

            state = STATE_DRAGGING;

        } else if (rightState == STATE_SETTLING) {

            state = STATE_SETTLING;

        } else {

            state = STATE_IDLE;

        }



        if (activeDrawer != null && activeState == STATE_IDLE) {

            if (mMenuOnScreen == 0 && !isDrawerClosed) {

                isDrawerClosed = true;

                if (mListener != null) {

                    mListener.onDrawerClosed(activeDrawer);

                }

            } else if (mMenuOnScreen == 1 && isDrawerClosed) {

                isDrawerClosed = false;

                if (mListener != null) {

                    mListener.onDrawerOpened(activeDrawer);

                }

            }

            if (isDrawerClosed) {

                closeDrawer();

            } else {

                openDrawer();

            }

        }

        if (state != mDrawerState) {

            mDrawerState = state;

            if (mListener != null) {

                mListener.onDrawerStateChanged(state);

            }

        }

    }



    public void addDrawerListener(DrawerListener listener) {

        mListener = listener;

    }



    @Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        setMeasuredDimension(widthSize, heightSize);

        // Content views get measured at exactly the layout's size.

        View contentView = getChildAt(0);

        MarginLayoutParams lp = (MarginLayoutParams) contentView.getLayoutParams();

        final int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize - lp.leftMargin - lp.rightMargin, MeasureSpec.EXACTLY);

        final int contentHeightSpec = MeasureSpec.makeMeasureSpec(heightSize - lp.topMargin - lp.bottomMargin, MeasureSpec.EXACTLY);

        contentView.measure(contentWidthSpec, contentHeightSpec);



        View menuView = getChildAt(1);

        lp = (MarginLayoutParams) menuView.getLayoutParams();

        final int drawerWidthSpec = getChildMeasureSpec(widthMeasureSpec, mMinDrawerMargin + lp.leftMargin + lp.rightMargin, lp.width);

        final int drawerHeightSpec = getChildMeasureSpec(heightMeasureSpec, lp.topMargin + lp.bottomMargin, lp.height);

        menuView.measure(drawerWidthSpec, drawerHeightSpec);



        mMenuView = menuView;

        mMainView = contentView;

    }



    @Override

    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        View contentView = mMainView;

        MarginLayoutParams lp = (MarginLayoutParams) contentView.getLayoutParams();

        contentView.layout(0, lp.topMargin,

                lp.leftMargin + contentView.getMeasuredWidth(),

                lp.topMargin + contentView.getMeasuredHeight());



        View menuView = mMenuView;

        lp = (MarginLayoutParams) menuView.getLayoutParams();

        final int width = r - l;

        final int childWidth = menuView.getMeasuredWidth();

        final int childHeight = menuView.getMeasuredHeight();

        int childLeft = width - (int) (childWidth * mMenuOnScreen);

        final float newOffset = (float) (width - childLeft) / childWidth;

        final boolean changeOffset = newOffset != mMenuOnScreen;

        menuView.layout(childLeft, lp.topMargin, childLeft + childWidth, lp.topMargin + childHeight);

        if (changeOffset) {

            setDrawerViewOffset(menuView, newOffset);

        }

        final int newVisibility = mMenuOnScreen > 0 ? VISIBLE : INVISIBLE;

        if (menuView.getVisibility() != newVisibility) {

            menuView.setVisibility(newVisibility);

        }

    }



    private float mLastMotionX;

    private float mLastMotionY;

    @Override

    public boolean onInterceptTouchEvent(MotionEvent ev) {

        //其实就是吧原来的实现放到一个新的方法里，然后添加自己的逻辑。

        final float x = ev.getX();

        final float y = ev.getY();



        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:

                mLastMotionX = x;

                mLastMotionY = y;

                break;

            case MotionEvent.ACTION_MOVE:

                //这里的判断拦截的逻辑是滑动的角度小于等于30°就是横向滑动，肯定拦截

                //否者使用原来的逻辑，调用interceptTouchEvent

                //这样写主要是有垂直滚动的RecyclewrView

                //具体怎么处理，看自己具体需求

                float distanceX = x - mLastMotionX;

                float distanceY = y - mLastMotionY;

                float xDiff = Math.abs(x - mLastMotionX);

                float yDiff = Math.abs(y - mLastMotionY);

                final int x_yDiff = (int) (xDiff * xDiff + yDiff * yDiff);



                boolean xMoved = x_yDiff > mTouchSlop * mTouchSlop;



                if(xMoved) {

                    if(xDiff > yDiff * 4) {

                        if ((mMenuOnScreen == 0 && distanceX > 0) || (mMenuOnScreen == 1 && distanceX < 0)) {

                            return super.onInterceptTouchEvent(ev);

                        }

                        return true;

                    }else {

                        final View touchedView = mHelper.findTopChildUnder((int) ev.getX(), (int) ev.getY());

                        if (touchedView != null && (touchedView == mMainView || touchedView == mMenuView) && mMenuOnScreen == 1) {

                            return super.onInterceptTouchEvent(ev);

                        }

                        return false;

                    }

                }



                /*if (xDiff > 0 && xDiff >= yDiff * Math.sqrt(1)) {

                    if ((mMenuOnScreen == 0 && distanceX > 0) || (mMenuOnScreen == 1 && distanceX < 0)) {

                        return super.onInterceptTouchEvent(ev);

                    }

                    return true;

                } else {

                    final View touchedView = mHelper.findTopChildUnder((int) ev.getX(), (int) ev.getY());

                    if (touchedView != null && (touchedView == mMainView || touchedView == mMenuView) && mMenuOnScreen == 1) {

                        return super.onInterceptTouchEvent(ev);

                    }

                }*/

        }

        return mHelper.shouldInterceptTouchEvent(ev);

    }





    @SuppressLint("ClickableViewAccessibility")

    @Override

    public boolean onTouchEvent(MotionEvent ev) {

        mHelper.processTouchEvent(ev);

        return true;

    }



    @Override

    public void computeScroll() {

        if (mHelper.continueSettling(true)) {

            ViewCompat.postInvalidateOnAnimation(this);

        }

    }



    public void closeDrawer() {

        View menuView = mMenuView;

        mMenuOnScreen = 0.f;

        mHelper.smoothSlideViewTo(menuView, getWidth(), 0);

        ViewCompat.postInvalidateOnAnimation(this);

    }



    public void openDrawer() {

        View menuView = mMenuView;

        mMenuOnScreen = 1.0f;

        mHelper.smoothSlideViewTo(menuView, getWidth() - menuView.getWidth(), menuView.getTop());

        ViewCompat.postInvalidateOnAnimation(this);

    }



    public Boolean isDrawerClosed() {

        return isDrawerClosed;

    }



    public int getMainWidth() {

        final int childWidth = mMenuView.getMeasuredWidth();

        return getWidth() - (int) (childWidth * mMenuOnScreen);

    }



    public int getMainHeight() {

        final int childWidth = mMenuView.getMeasuredWidth();

        return getHeight() - (int) (childWidth * mMenuOnScreen);

    }



    public float getMenuOnScreen() {

        return mMenuOnScreen;

    }



    @Override

    protected LayoutParams generateDefaultLayoutParams() {

        return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

    }



    public LayoutParams generateLayoutParams(AttributeSet attrs) {

        return new MarginLayoutParams(getContext(), attrs);

    }



    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {

        return new MarginLayoutParams(p);

    }



    public int getSWidth() {

        return Resources.getSystem().getDisplayMetrics().widthPixels;

    }



   /* public int getSHeight() {

        return Resources.getSystem().getDisplayMetrics().heightPixels;

    }*/



    /**

     * Indicates that any drawers are in an idle, settled state. No animation is in progress.

     */

    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;



    /**

     * Indicates that a drawer is currently being dragged by the user.

     */

    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;



    /**

     * Indicates that a drawer is in the process of settling to a final position.

     */

    public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING;





    @IntDef({STATE_IDLE, STATE_DRAGGING, STATE_SETTLING})

    @Retention(RetentionPolicy.SOURCE)

    private @interface State {

    }



    /**

     * Listener for monitoring events about drawers.

     */

    public interface DrawerListener {

        /**

         * Called when a drawer's position changes.

         *

         * @param drawerView  The child view that was moved

         * @param slideOffset The new offset of this drawer within its range, from 0-1

         */

        void onDrawerSlide(@NonNull View drawerView, float slideOffset);



        /**

         * Called when a drawer has settled in a completely open state.

         * The drawer is interactive at this point.

         *

         * @param drawerView Drawer view that is now open

         */

        void onDrawerOpened(@NonNull View drawerView);



        /**

         * Called when a drawer has settled in a completely closed state.

         *

         * @param drawerView Drawer view that is now closed

         */

        void onDrawerClosed(@NonNull View drawerView);



        /**

         * Called when the drawer motion state changes. The new state will

         * be one of {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}.

         *

         * @param newState The new drawer motion state

         */

        void onDrawerStateChanged(@RightDrawerLayout.State int newState);

    }

}

